/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 */

#ifndef __PQ_SceneObject_H__
#define __PQ_SceneObject_H__

#include "Object.h"
#include "MeshObject.h"
#include "MeshData.h"

namespace PQEngine
{

	/** Used to get and set  Scene render property
        @remarks
			
    */
	class SceneRenderProperty
	{
	private:
		Ogre::PolygonMode _mode;//0 points 1 wireframe 2 solid
		Ogre::ColourValue _backgroundColor;
		Ogre::ColourValue _ambient;
		Ogre::ColourValue _fogColor;
		
		float _fogDensity;
		SceneObject* _sceneObject;
	public:
		SceneRenderProperty(SceneObject* obj);

		inline Ogre::ColourValue& getBackground()
		{
			return _backgroundColor;
		}

		inline Ogre::ColourValue& ambient()
		{
			return _ambient;
		}

		inline Ogre::ColourValue& getFogColor()
		{
			return _fogColor;
		}

		inline float getFogDensity()
		{
			return _fogDensity;
		}

		void setBackground(Ogre::ColourValue color);
		void setAmbient(Ogre::ColourValue color);
		//void setFogColor(Ogre::ColourValue color);
		

		void setPolyMode(Ogre::PolygonMode mode);
	};


	/** A SceneObject is an independent scene container.
        @remarks
			This is used to manage a whole scene
    */
	class SceneObject
		: public Object
	{
	public:
		//use friend to implement property change
		friend void SceneRenderProperty::setBackground(Ogre::ColourValue color);
		friend void SceneRenderProperty::setAmbient(Ogre::ColourValue color);
		friend void SceneRenderProperty::setPolyMode(Ogre::PolygonMode type);
		typedef std::map<Ogre::String,GameObject*> GameObjectMap;//used search objects by name
	protected:
		void setBackground(Ogre::ColourValue color);

		inline void setAmbient(Ogre::ColourValue color)
		{
			if(_sceneManager){
				_sceneManager->setAmbientLight(color);
			}
		}

		void setPolyMode(Ogre::PolygonMode mode);

	public:
		SceneObject(Ogre::RenderWindow* window,Ogre::SceneType sceneType);
		~SceneObject();

		inline Ogre::RaySceneQuery* createRayQuery(const Ogre::Ray& ray, unsigned long mask = 0xFFFFFFFF)
		{
			if(_sceneManager){
				return _sceneManager->createRayQuery(ray,mask);
			}
			return 0;
		}

		/*Activate the scene.
		*/
		void activate();

		/* Update scene in seconds
		*/
		virtual void update(float deltaT);

		/*Load Ogre mesh object into this scene
		*/
		MeshObject* loadMeshObject(std::string meshName);


		 /** Create an Entity (instance of a discrete mesh).
            @param
                entityName The name to be given to the entity (must be unique).
            @param
                meshName The name of the Mesh it is to be based on (e.g. 'knot.oof'). The
                mesh will be loaded if it is not already.
        */
		virtual Ogre::Entity* createEntity(const Ogre::String& entityName, const Ogre::String& meshName);


		/** Create an Entity (instance of a discrete mesh) with an autogenerated name.
            @param
                meshName The name of the Mesh it is to be based on (e.g. 'knot.oof'). The
                mesh will be loaded if it is not already.
        */
		virtual Ogre::Entity* createEntity(const Ogre::String& meshName);

		void enableSkyDome(bool enabled);

        /** Creates a new named SceneNode as a child of camera node.
        */
		Ogre::SceneNode* createCameraFollowSceneNode(const Ogre::String& name, const Ogre::Vector3& translate = Ogre::Vector3::ZERO, const Ogre::Quaternion& rotate = Ogre::Quaternion::IDENTITY);

		/** Creates a new unnamed SceneNode as a child of camera node.
        */
		Ogre::SceneNode* createCameraFollowSceneNode(const Ogre::Vector3& translate = Ogre::Vector3::ZERO, const Ogre::Quaternion& rotate = Ogre::Quaternion::IDENTITY );

		GameObject* getRoot();

		/*Select game object by mouse on viewport screen in current scene.
		@param x The mouse position x in the screen.
		@param y The mouse position y in the screen.
		*/
		GameObject* selectObject(int x,int y);
		
		/*GET(not select) game object by mouse on viewport screen in current scene.
		@param x The mouse position x in the screen.
		@param y The mouse position y in the screen.
		*/
		GameObject* getObject(int x,int y);

		/*GET(not select) game object by mouse ray on viewport screen in current scene.
		@param ray The mouse ray shot from mouse pointer.
		*/
		GameObject* getObject(Ogre::Ray ray);

		/*Select game object by pointer in current scene.
		@param object The game object's pointer.
		*/
		GameObject* selectObject(GameObject* object);

		/*Select game object by id in current scene.
		@param id The game object's object id.
		*/
		GameObject* selectObject(int id);
		
		
		/*Get mouse ray by screen coord
		*/
		Ogre::Ray getRay(int x,int y);

		/*Get center screen mouse ray 
		*/
		Ogre::Ray getRay();

		Ogre::SceneNode* createChildSceneNode();

		inline Ogre::SceneNode* getRootSceneNode()
		{
			Ogre::SceneNode* node=_sceneManager->getRootSceneNode();
			return node;
		}

		inline static bool isActivate(SceneObject* sceneObject)
		{
			if(!sceneObject){
				return false;
			}
			return sceneObject->isActivate();
		}

		inline static void setActiveScene(SceneObject* sceneObject)
		{
			if(sceneObject==NULL){
				return;
			}

			if(_activeScene){
				_activeScene->setIsActivate(false);
			}
			sceneObject->setIsActivate(true);
			
		}

		/* Static method to get actived scene object
			@remark
			At a specific time only ONE SceneObject is Actived.
		*/
		inline static SceneObject* getActive()
		{
			assert(_activeScene);
			return _activeScene;
		}
		

		inline bool hasObject(Ogre::String name)
		{
			GameObjectMap::iterator it= _gameObjects.find(name);
			if(it!=_gameObjects.end()){
				return true;
			}
			return false;
		}
		
		/*Get the render window that this scene is render into.
		*/
		Ogre::RenderWindow* getRenderWindow();

		/*Create a flat terrain chunk in position slotX slotY with diffuse texture index in terrain texture GPK archive.
		@param slotX The X coord of slot position.
		@param slotX The Y coord of slot position.
		@param diffuseTexIndex The diffuse texture index in terrain texture GPK archive.
		*/
		//TerrainObject* createFlatTerrain(int slotX,int slotY,int diffuseTexIndex);
		void createSphereObject(Ogre::Vector3 position);

		//get current selected game object
		GameObject* getGameObject();

		/*Must change static active SceneObject
		*/
		inline void setIsActivate(bool b)
		{
			_isActivate= b;
			if(_isActivate){
				_activeScene=this;
			}
		}

		inline bool isActivate()
		{
			return _isActivate;
		}

		inline CameraObject* getCameraObject()
		{
			return _cameraObject;
		}

		void resetScreenResolution(int width,int height);

		inline Ogre::SceneManager* getSceneManager()
		{
			return _sceneManager;
		}

		inline SceneRenderProperty& getRenderProperty()
		{
			return *_renderProperty;//right to do so?
		}
		
		/** Gets the number of triangles rendered in the last update() call. */
		inline int getTriangleCount()
		{
			assert(!"Not implemented.");//RenderWindow get
			
			return 0;
		}

	protected:
			/*load default scene.
		*/
		virtual void loadScene(Ogre::SceneType sceneType);
	protected:
		static SceneObject* _activeScene;
		bool _isActivate;
		

		GameObject* _rootObject;//hold root gameobject
		GameObject* _selectedObject;//current selected gameobject to manipulate
		CameraObject* _cameraObject;
		MeshData* _selectMeshData;//used load selected mesh and test selection

		GameObjectMap _gameObjects;
		SceneRenderProperty* _renderProperty;

		Ogre::SceneManager* _sceneManager;
		Ogre::RaySceneQuery* _raySceneQuery;
		Ogre::RenderWindow* _window;//the render window that the scene render into
	
		Ogre::SceneType _sceneType;

	public:
		//Please inject input into scene.
		void OnMouseMove(int x,int y);
	private:
		int _mouseX;
		int _mouseY;
		bool _inited;//inited (load default scene)
	};

}
#endif
